package com.explorex.infra.puma.gateway.netty.ws.message.handler;

import com.explorex.infra.puma.gateway.netty.common.security.AESUtil;
import com.explorex.infra.puma.gateway.netty.common.utils.JacksonObjectMapper;
import com.explorex.infra.puma.gateway.netty.exeptions.MessageParseException;
import com.explorex.infra.puma.gateway.netty.ws.WebSocketSession;
import com.explorex.infra.puma.gateway.netty.ws.enums.ContentTypeEnum;
import com.explorex.infra.puma.gateway.netty.ws.enums.MessageTypeEnum;
import com.explorex.infra.puma.gateway.netty.ws.message.Message;
import com.explorex.infra.puma.gateway.netty.ws.message.MessageBody;
import com.explorex.infra.puma.gateway.netty.ws.message.MessageHeader;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Preconditions;

import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.explorex.infra.puma.gateway.netty.common.enums.LonglinkErrorEnum.*;


/**
 * @author: JacceYang
 * @createTime: 2022/07/15 11:14 上午
 * @description: 消息处理器
 */
@Slf4j
public class MessageCenter {

    private final List<BaseProcessor> baseProcessors = new ArrayList<>();

    public MessageCenter() {
        this.registerProcessor(new PingMessageProcessor());
        this.registerProcessor(new AuthMessageProcessor());
        this.registerProcessor(new CloseChannelMessageProcessor());
        this.registerProcessor(new StatusChangeMessageProcessor());
        this.registerProcessor(new BindMessageProcessor());
        this.registerProcessor(new MessageTypeProcessor());
        this.registerProcessor(new MessageDownTypeProcessor());
    }

    private static Message errorResponseMessage(Message originMessage, int code, String msg) {

        if (originMessage.getHeader() == null) {
            originMessage.setHeader(new MessageHeader(MessageTypeEnum.UNKNOWN.getType()));
        }
        short reqType = originMessage.getHeader().getType();
        short responseType = (short) (reqType | 0x80);
        originMessage.getHeader().setType(responseType);
        EmptyMessageProcessor.EmptyMessage defaultMessage = new EmptyMessageProcessor.EmptyMessage();
//        暂时关闭，等端上盖好后放开即可
//        defaultMessage.setCode(code);
        defaultMessage.setMsg(Optional.ofNullable(msg).orElse("internal server error"));
        originMessage.setBody(defaultMessage);
        return originMessage;
    }

    private void registerProcessor(BaseProcessor process) {
        baseProcessors.add(process);
    }

    private BaseProcessor getProcessor(short type) {
        Optional<BaseProcessor> process = baseProcessors.stream().filter(ele -> ele.getMessageType() == type).findAny();
        return process.isPresent() ? process.get() : EmptyMessageProcessor.EMPTY_PROCESSOR;
    }

    public void parseAndProcessMessage(WebSocketSession session, String msgStr, Message messageAck) {
        Message messageReq = new Message();
        try {
            long startTime = System.currentTimeMillis();
            //todo 加密的时候把这个打开,上面一条删掉
            parseMessage(session, msgStr, messageReq);
//            parseMessage(session, new String(decryptMessage(session.getShareKey(), msgStr)), messageReq);
            // 消息合法性检查
            checkMessageFormat(messageReq.getHeader());
            // 处理消息
            Message messageResult = getProcessor(messageReq.getHeader().getType()).process(session, messageReq);
            if (messageResult != null) {
                messageAck.setBody(messageResult.getBody());
                messageAck.setHeader(messageResult.getHeader());
            }

            // report message
        } catch (IllegalArgumentException e) {
            if (messageAck.getHeader() == null) {
                messageAck.setHeader(messageReq.getHeader());
            }
            errorResponseMessage(messageAck, ERR_COMM_INVALID_PARAMETERS.getValue(), e.getMessage());
        } catch (MessageParseException e) {
            if (messageAck.getHeader() == null) {
                messageAck.setHeader(messageReq.getHeader());
            }
            errorResponseMessage(messageAck, e.getCode(), e.getMessage());
        } catch (Exception e) {
            log.error("parseAndProcessMessage exception msgStr={}", msgStr, e);
            if (messageAck.getHeader() == null) {
                messageAck.setHeader(messageReq.getHeader());
            }
            errorResponseMessage(messageAck, -1, e.getMessage());
        }
    }

    private byte[] decryptMessage(byte[] shareKey, String rawMsg) throws GeneralSecurityException {
        return AESUtil.decrypt(shareKey, rawMsg.getBytes(StandardCharsets.ISO_8859_1));
    }

    private void parseMessage(WebSocketSession session, String msgStr, Message message) {
        MessageHeader messageHeader = null;
        try {
            JsonNode messageJsonNode = JacksonObjectMapper.getJsonTree(msgStr);
            String bodyStr = messageJsonNode.get("body") == null ? null : messageJsonNode.get("body").toString();
            JsonNode header = messageJsonNode.get("header");
            if (header == null) {
                session.sendMessage("helel");
                throw new MessageParseException(ERR_SDK_NET_PROTOCOL_HEADER_INVALID.getValue(), ERR_SDK_NET_PROTOCOL_HEADER_INVALID.getDesc());
            }
            String headerStr = header.toString();
            // 消息头解析
            messageHeader = JacksonObjectMapper.loadObject(headerStr, MessageHeader.class);
            // 检查消息格式
            if (messageHeader == null) {
                log.warn("invalid msg format for parse");
                throw new MessageParseException(ERR_SDK_NET_PROTOCOL_HEADER_INVALID.getValue(), ERR_SDK_NET_PROTOCOL_HEADER_INVALID.getDesc());
            }
            message.setHeader(messageHeader);
            messageHeader.setRemoteIp(session.getRemoteIp());
            // 消息体解析
            BaseProcessor processor = getProcessor(messageHeader.getType());
            MessageBody messageBody = processor.parseBody(messageHeader.getContentType(), bodyStr);
            // 消息装配返回
            message.setBody(messageBody);
        } catch (JsonParseException e) {
            throw new MessageParseException(ERR_COMM_INVALID_JSON.getValue(), ERR_COMM_INVALID_JSON.getDesc());
        } catch (MessageParseException e) {
            throw e;
        } catch (NullPointerException e) {
            throw new MessageParseException(ERR_SDK_NET_PROTOCOL_BODY_INVALID.getValue(), ERR_SDK_NET_PROTOCOL_BODY_INVALID.getDesc());
        }catch (IllegalArgumentException e){
            throw new MessageParseException(ERR_COMM_INVALID_PARAMETERS.getValue(), ERR_COMM_INVALID_PARAMETERS.getDesc());
        } catch (Exception e) {
            log.warn("parseMessage exception msgStr={},messageHeader={}", msgStr, messageHeader, e);
            throw new MessageParseException(ERR_COMM_INVALID_JSON.getValue(), ERR_COMM_INVALID_JSON.getDesc());
        }
    }

    private void checkMessageFormat(MessageHeader messageHeader) {
        Preconditions.checkArgument(MessageTypeEnum.findByType(messageHeader.getType()) != MessageTypeEnum.UNKNOWN
                , "parameter error,invalid message type,type=%s", messageHeader.getType());

//        if (MessageTypeEnum.findByType(messageHeader.getType()) == MessageTypeEnum.AUTH) {
//            Preconditions.checkArgument(DomainEnum.findByValue(messageHeader.getDomain()) != DomainEnum.UNDEFINED
//                    , "domain is invalid,domain=%s", messageHeader.getDomain());
//        }

    }
}
